{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# `Nuqleon.Reflection.Virtualization`\n",
        "\n",
        "Provides interfaces for the .NET reflection APIs, enabling the construction of wrappers, alternative implementations, mocks, etc. One example of a wrapper over reflection is a memoizing cache to speed up expensive reflection operations."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Reference the library"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Option 1 - Use a local build\n",
        "\n",
        "If you have built the library locally, run the following cell to load the latest build."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [],
      "source": [
        "#r \"bin/Debug/net6.0/Nuqleon.Reflection.Virtualization.dll\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Option 2 - Use NuGet packages\n",
        "\n",
        "If you want to use the latest published package from NuGet, run the following cell."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [],
      "source": [
        "#r \"nuget:Nuqleon.Reflection.Virtualization,*-*\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## (Optional) Attach a debugger\n",
        "\n",
        "If you'd like to step through the source code of the library while running samples, run the following cell, and follow instructions to start a debugger (e.g. Visual Studio). Navigate to the source code of the library to set breakpoints."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [],
      "source": [
        "System.Diagnostics.Debugger.Launch();"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## `IReflectionProvider`\n",
        "\n",
        "This library provides abstractions over the .NET reflection APIs using a whole bunch of fine-grained interfaces that represent different aspects of reflection. In the cell below, we reflect on the library to show all of the interfaces."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": "interface IAssemblyIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IAssemblyLoadingProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IComTypeLoadingProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IEventInfoIntrospectionProvider : IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IFieldInfoIntrospectionProvider : IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IMethodBaseIntrospectionProvider : IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IMethodCreationProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IMethodInfoIntrospectionProvider : IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IModuleIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IModuleLoadingProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IParameterInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IPropertyInfoIntrospectionProvider : IMemberInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionCreationProvider : ITypeCreationProvider, IMethodCreationProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionHandlerResolver;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionIntrospectionProvider : IAssemblyIntrospectionProvider, IModuleIntrospectionProvider, ITypeIntrospectionProvider, IMemberInfoIntrospectionProvider, IReflectionTypeSystemProvider, IFieldInfoIntrospectionProvider, IMethodInfoIntrospectionProvider, IMethodBaseIntrospectionProvider, IPropertyInfoIntrospectionProvider, IEventInfoIntrospectionProvider, IParameterInfoIntrospectionProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionInvocationProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionLoadingProvider : IAssemblyLoadingProvider, IModuleLoadingProvider, ITypeLoadingProvider, IComTypeLoadingProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionProvider : IReflectionIntrospectionProvider, IAssemblyIntrospectionProvider, IModuleIntrospectionProvider, ITypeIntrospectionProvider, IMemberInfoIntrospectionProvider, IReflectionTypeSystemProvider, IFieldInfoIntrospectionProvider, IMethodInfoIntrospectionProvider, IMethodBaseIntrospectionProvider, IPropertyInfoIntrospectionProvider, IEventInfoIntrospectionProvider, IParameterInfoIntrospectionProvider, IReflectionCreationProvider, ITypeCreationProvider, IMethodCreationProvider, IReflectionInvocationProvider, IReflectionLoadingProvider, IAssemblyLoadingProvider, IModuleLoadingProvider, ITypeLoadingProvider, IComTypeLoadingProvider, IReflectionHandlerResolver;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface IReflectionTypeSystemProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface ITypeCreationProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface ITypeIntrospectionProvider : IMemberInfoIntrospectionProvider, IReflectionTypeSystemProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "interface ITypeLoadingProvider : IComTypeLoadingProvider;\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "using System.Reflection;\n",
        "\n",
        "foreach (var t in typeof(IReflectionProvider).Assembly.GetTypes().Where(t => t.IsInterface).OrderBy(t => t.Name))\n",
        "{\n",
        "    var declaration = \"interface \" + t.Name;\n",
        "\n",
        "    var ifs = t.GetInterfaces();\n",
        "    if (ifs.Length > 0)\n",
        "    {\n",
        "        declaration += \" : \" + string.Join(\", \", ifs.Select(t => t.Name));\n",
        "    }\n",
        "\n",
        "    declaration += \";\";\n",
        "\n",
        "    Console.WriteLine(declaration);\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The presence of fine-grained interfaces enables components to take a dependency on just the aspects of reflection they rely on. At the top of the interface hierarchy, the `IReflectionProvider` interface unions all of these aspects together."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Two implementations of `IReflectionProvider` are provided in the library:\n",
        "\n",
        "* `DefaultReflectionProvider` implements the interface using the built-in .NET reflection APIs, providing virtual methods that can be overridden.\n",
        "* `CachingDefaultReflectionProvider` which uses memoization techniques from `Nuqleon.Memory` to speed up repeated reflection API invocations.\n",
        "\n",
        "Using these base classes, one can alter reflection behavior by overriding methods, e.g. to intercept calls to `GetType` used to load a type by name. This is used in `Reaqtor` to redirect types that have been relocated to other assemblies or even have been renamed."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "In the cell below, we'll just have a quick look at the `DefaultReflectionProvider` to call some reflection API."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": "System.String, System.Int32\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "IReflectionProvider defaultProvider = DefaultReflectionProvider.Instance;\n",
        "\n",
        "IReadOnlyList<Type> listOfInt = defaultProvider.GetGenericArguments(typeof(Dictionary<string, int>));\n",
        "\n",
        "Console.WriteLine(string.Join(\", \", listOfInt));"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note that the return type of `GetGenericArguments` on the interface is not `Type[]` but `IReadOnlyList<Type>`. This differs from the reflection APIs but matches the 99.9% use case of these APIs, where the caller does not mutate the resulting array of types. This in turn enables us to build caching reflection providers that can avoid the allocation cost. In the cell below, we show the use of the `CachingReflectionProvider`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": "System.String, System.Int32\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "using System.Memory;\n",
        "\n",
        "IMemoizer mem = Memoizer.Create(MemoizationCacheFactory.Unbounded);\n",
        "\n",
        "IReflectionProvider cachingProvider = new CachingDefaultReflectionProvider(mem);\n",
        "\n",
        "IReadOnlyList<Type> listOfInt = cachingProvider.GetGenericArguments(typeof(Dictionary<string, int>));\n",
        "\n",
        "Console.WriteLine(string.Join(\", \", listOfInt));"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "To show the effect of caching, let's refactor the code a tiny bit."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [],
      "source": [
        "static void Test(IReflectionProvider provider) => provider.GetGenericArguments(typeof(Dictionary<string, int>));"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We'll also leverage `Nuqleon.Time` facilities to measure elapsed time and memory allocations."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [],
      "source": [
        "using System.Time;\n",
        "\n",
        "class MemoryClock : IClock\n",
        "{\n",
        "    public long Now => GC.GetAllocatedBytesForCurrentThread();\n",
        "}\n",
        "\n",
        "IStopwatch swMem = StopwatchFactory.FromClock(new MemoryClock()).Create();\n",
        "IStopwatch swTime = StopwatchFactory.Diagnostics.Create();"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "With the test code and stopwatches in place, we can write a quick benchmark utility."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "dotnet_interactive": {
          "language": "csharp"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": "default\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "40000120 bytes allocated; 107 ms spent.\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "caching\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "288 bytes allocated; 75 ms spent.\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": "\r\n"
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "void Benchmark(IReflectionProvider provider, int n)\n",
        "{\n",
        "    swMem.Restart();\n",
        "    swTime.Restart();\n",
        "    {\n",
        "        for (int i = 0; i < n; i++)\n",
        "        {\n",
        "            Test(provider);\n",
        "        }\n",
        "    }\n",
        "    swTime.Stop();\n",
        "    swMem.Stop();\n",
        "}\n",
        "\n",
        "foreach (var (name, provider) in new[]\n",
        "{\n",
        "    (\"default\", defaultProvider),\n",
        "    (\"caching\", cachingProvider),\n",
        "})\n",
        "{\n",
        "    Console.WriteLine(name);\n",
        "    Benchmark(provider, 1_000_000);\n",
        "    Console.WriteLine($\"{swMem.ElapsedTicks} bytes allocated; {swTime.ElapsedMilliseconds} ms spent.\");\n",
        "    Console.WriteLine();\n",
        "}"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".NET (C#)",
      "language": "C#",
      "name": ".net-csharp"
    },
    "language_info": {
      "file_extension": ".cs",
      "mimetype": "text/x-csharp",
      "name": "C#",
      "pygments_lexer": "csharp",
      "version": "9.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}